Python λ°μ΄ν° ν΄λμ€μ κ³ κΈ κΈ°λ₯μ μ΄ν΄λ³΄κ³ , κΈλ‘λ² μ¬μ©μλ₯Ό μν μ κ΅νκ³ μ μ°ν λ°μ΄ν° λͺ¨λΈλ§μ μν΄ νλ ν©ν 리 ν¨μμ μμμ λΉκ΅ν©λλ€.
λ°μ΄ν° ν΄λμ€ κ³ κΈ κΈ°λ₯: μ μ°ν λ°μ΄ν° λͺ¨λΈλ§μ μν νλ ν©ν 리 ν¨μ vs. μμ
Python 3.7μ λμ
λ Pythonμ dataclasses
λͺ¨λμ κ°λ°μκ° λ°μ΄ν° μ€μ¬ ν΄λμ€λ₯Ό μ μνλ λ°©μμ νμ νμ΅λλ€. μμ±μ, νν λ©μλ λ° λλ±μ± κ²μ¬μ κ΄λ ¨λ μμ©κ΅¬ μ½λλ₯Ό μ€μμΌλ‘μ¨ λ°μ΄ν° ν΄λμ€λ λ°μ΄ν°λ₯Ό λͺ¨λΈλ§νλ κΉλνκ³ ν¨μ¨μ μΈ λ°©λ²μ μ 곡ν©λλ€. κ·Έλ¬λ κΈ°λ³Έ μ¬μ©λ²μ λμ΄ κ³ κΈ κΈ°λ₯μ μ΄ν΄νλ κ²μ νΉν λ€μν μꡬ μ¬νμ΄ μΌλ°μ μΈ κΈλ‘λ² κ°λ° νκ²½μμ μ κ΅νκ³ μ μ κ°λ₯ν λ°μ΄ν° ꡬ쑰λ₯Ό ꡬμΆνλ λ° λ§€μ° μ€μν©λλ€. μ΄ κ²μκΈμμλ λ°μ΄ν° ν΄λμ€λ₯Ό μ¬μ©νμ¬ κ³ κΈ λ°μ΄ν° λͺ¨λΈλ§μ λ¬μ±νκΈ° μν λ κ°μ§ κ°λ ₯ν λ©μ»€λμ¦μΈ νλ ν©ν 리 ν¨μμ μμμ μμΈν μ΄ν΄λ΄
λλ€. μ μ°μ±κ³Ό μ μ§ κ΄λ¦¬ μΈ‘λ©΄μμ κ·Έ λμμ€, μ¬μ© μ¬λ‘ λ° λΉκ΅ λ°©λ²μ μ΄ν΄λ΄
λλ€.
λ°μ΄ν° ν΄λμ€μ ν΅μ¬ μ΄ν΄
κ³ κΈ κΈ°λ₯μ μ΄ν΄λ³΄κΈ° μ μ λ°μ΄ν° ν΄λμ€λ₯Ό ν¨κ³Όμ μΌλ‘ λ§λλ μμλ₯Ό κ°λ΅νκ² μμ½ν΄ λ³΄κ² μ΅λλ€. λ°μ΄ν° ν΄λμ€λ μ£Όλ‘ λ°μ΄ν°λ₯Ό μ μ₯νλ λ° μ¬μ©λλ ν΄λμ€μ
λλ€. @dataclass
λ°μ½λ μ΄ν°λ ν΄λμ€ λ΄μ μ μλ μ ν μ£Όμμ΄ λ¬λ¦° νλλ₯Ό κΈ°λ°μΌλ‘ __init__
, __repr__
λ° __eq__
μ κ°μ νΉμ λ©μλλ₯Ό μλμΌλ‘ μμ±ν©λλ€. μ΄ μλνλ μ½λλ₯Ό ν¬κ² μ 리νκ³ μΌλ°μ μΈ λ²κ·Έλ₯Ό λ°©μ§ν©λλ€.
κ°λ¨ν μλ₯Ό λ€μ΄ λ³΄κ² μ΅λλ€.
from dataclasses import dataclass
@dataclass
class User:
user_id: int
username: str
is_active: bool = True
# Usage
user1 = User(user_id=101, username="alice")
user2 = User(user_id=102, username="bob", is_active=False)
print(user1) # Output: User(user_id=101, username='alice', is_active=True)
print(user1 == User(user_id=101, username="alice")) # Output: True
μ΄ λ¨μμ±μ κ°λ¨ν λ°μ΄ν° ννμ λ§€μ° μ ν©ν©λλ€. κ·Έλ¬λ νλ‘μ νΈκ° 볡μ‘ν΄μ§κ³ μ¬λ¬ μ§μμ λ€μν λ°μ΄ν° μμ€ λλ μμ€ν κ³Ό μνΈ μμ©ν¨μ λ°λΌ λ°μ΄ν° μ§ν λ° κ΅¬μ‘°λ₯Ό κ΄λ¦¬νλ €λ©΄ λ³΄λ€ κ³ κΈ κΈ°μ μ΄ νμν©λλ€.
νλ ν©ν 리 ν¨μλ₯Ό μ¬μ©ν κ³ κΈ λ°μ΄ν° λͺ¨λΈλ§
dataclasses
λͺ¨λμ field()
ν¨μλ₯Ό ν΅ν΄ νμ©λλ νλ ν©ν 리 ν¨μλ μΈμ€ν΄μ€ν μ€μ λ³κ²½ κ°λ₯νκ±°λ κ³μ°μ΄ νμν νλμ λν κΈ°λ³Έκ°μ μ§μ νλ λ°©λ²μ μ 곡ν©λλ€. λͺ©λ‘ λλ μ¬μ κ³Ό κ°μ λ³κ²½ κ°λ₯ν κ°μ²΄λ₯Ό κΈ°λ³Έκ°μΌλ‘ μ§μ ν λΉνλ λμ (μΈμ€ν΄μ€ κ°μ μκΈ°μΉ μμ 곡μ μνκ° λ°μν μ μμ) ν©ν 리 ν¨μλ μ κ°μ²΄λ§λ€ κΈ°λ³Έκ°μ μ μΈμ€ν΄μ€κ° μμ±λλλ‘ ν©λλ€.
ν©ν 리 ν¨μλ₯Ό μ¬μ©νλ μ΄μ ? λ³κ²½ κ°λ₯ν κΈ°λ³Έκ°μ ν¨μ
μΌλ° Python ν΄λμ€μ μΌλ°μ μΈ μ€μλ λ³κ²½ κ°λ₯ν κΈ°λ³Έκ°μ μ§μ ν λΉνλ κ²μ λλ€.
# Problematic approach with standard classes (and dataclasses without factories)
class ShoppingCart:
def __init__(self):
self.items = [] # All instances will share this same list!
cart1 = ShoppingCart()
cart2 = ShoppingCart()
cart1.items.append("apple")
print(cart2.items) # Output: ['apple'] - unexpected!
λ°μ΄ν° ν΄λμ€λ μ΄μ λ©΄μμ΄ μμ΅λλ€. λ³κ²½ κ°λ₯ν κΈ°λ³Έκ°μ μ§μ μ€μ νλ €κ³ νλ©΄ λμΌν λ¬Έμ κ° λ°μν©λλ€.
from dataclasses import dataclass
@dataclass
class ProductInventory:
product_name: str
# WRONG: mutable default
# stock_levels: dict = {}
# stock1 = ProductInventory(product_name="Laptop")
# stock2 = ProductInventory(product_name="Mouse")
# stock1.stock_levels["warehouse_A"] = 100
# print(stock2.stock_levels) # {'warehouse_A': 100} - unexpected!
field(default_factory=...)
μκ°
default_factory
μΈμμ ν¨κ» μ¬μ©λ λ field()
ν¨μλ μ΄ λ¬Έμ λ₯Ό κΉλνκ² ν΄κ²°ν©λλ€. κΈ°λ³Έκ°μ μμ±νκΈ° μν΄ μΈμ μμ΄ νΈμΆλ μ μλ νΈμΆ κ°λ₯ κ°μ²΄ (μΌλ°μ μΌλ‘ ν¨μ λλ ν΄λμ€ μμ±μ)λ₯Ό μ 곡ν©λλ€.
μμ : ν©ν 리 ν¨μλ₯Ό μ¬μ©ν μ¬κ³ κ΄λ¦¬
ν©ν 리 ν¨μλ₯Ό μ¬μ©νμ¬ ProductInventory
μμ λ₯Ό κ°μ ν΄ λ³΄κ² μ΅λλ€.
from dataclasses import dataclass, field
@dataclass
class ProductInventory:
product_name: str
# Correct approach: use a factory function for the mutable dict
stock_levels: dict = field(default_factory=dict)
# Usage
stock1 = ProductInventory(product_name="Laptop")
stock2 = ProductInventory(product_name="Mouse")
stock1.stock_levels["warehouse_A"] = 100
stock1.stock_levels["warehouse_B"] = 50
stock2.stock_levels["warehouse_A"] = 200
print(f"Laptop stock: {stock1.stock_levels}")
# Output: Laptop stock: {'warehouse_A': 100, 'warehouse_B': 50}
print(f"Mouse stock: {stock2.stock_levels}")
# Output: Mouse stock: {'warehouse_A': 200}
# Each instance gets its own distinct dictionary
assert stock1.stock_levels is not stock2.stock_levels
μ΄λ₯Ό ν΅ν΄ κ° ProductInventory
μΈμ€ν΄μ€λ κ΅μ°¨ μΈμ€ν΄μ€ μ€μΌμ λ°©μ§νλ©΄μ μ¬κ³ μμ€μ μΆμ νκΈ° μν κ³ μ ν μ¬μ μ μ»κ² λ©λλ€.
ν©ν 리 ν¨μμ μΌλ°μ μΈ μ¬μ© μ¬λ‘:
- λͺ©λ‘ λ° μ¬μ : κ° μΈμ€ν΄μ€μ κ³ μ ν νλͺ© 컬λ μ μ μ μ₯νλ λ° μ¬μ©λλ κ²μΌλ‘ μ μ¦λμμ΅λλ€.
- μΈνΈ: λ³κ²½ κ°λ₯ν νλͺ©μ κ³ μ ν 컬λ μ μ λλ€.
- νμμ€ν¬ν: μμ± μκ°μ λν κΈ°λ³Έ νμμ€ν¬νλ₯Ό μμ±ν©λλ€.
- UUID: κ³ μ μλ³μλ₯Ό λ§λλλ€.
- 볡μ‘ν κΈ°λ³Έ κ°μ²΄: λ€λ₯Έ 볡μ‘ν κ°μ²΄λ₯Ό κΈ°λ³Έκ°μΌλ‘ μΈμ€ν΄μ€νν©λλ€.
μμ : κΈ°λ³Έ νμμ€ν¬ν
λ§μ κΈλ‘λ² μ ν리μΌμ΄μ
μμ μμ± λλ μμ μκ°μ μΆμ νλ κ²μ΄ νμμ μ
λλ€. λ€μμ datetime
κ³Ό ν¨κ» ν©ν 리 ν¨μλ₯Ό μ¬μ©νλ λ°©λ²μ
λλ€.
from dataclasses import dataclass, field
from datetime import datetime
@dataclass
class EventLog:
event_id: int
description: str
# Factory for current timestamp
timestamp: datetime = field(default_factory=datetime.now)
# Usage
event1 = EventLog(event_id=1, description="User logged in")
# A small delay to see timestamp differences
import time
time.sleep(0.01)
event2 = EventLog(event_id=2, description="Data processed")
print(f"Event 1 timestamp: {event1.timestamp}")
print(f"Event 2 timestamp: {event2.timestamp}")
# Notice the timestamps will be slightly different
assert event1.timestamp != event2.timestamp
μ΄ μ κ·Ό λ°©μμ κ°λ ₯νλ©° κ° μ΄λ²€νΈ λ‘κ·Έ νλͺ©μ΄ μμ±λ μ νν μκ°μ μΊ‘μ²νλλ‘ ν©λλ€.
κ³ κΈ ν©ν 리 μ¬μ©λ²: μ¬μ©μ μ§μ μ΄κΈ°νκΈ°
λλ€ ν¨μ λλ λ 볡μ‘ν ν¨μλ₯Ό ν©ν λ¦¬λ‘ μ¬μ©ν μλ μμ΅λλ€.
from dataclasses import dataclass, field
def create_default_settings():
# In a global app, these might be loaded from a config file based on locale
return {"theme": "light", "language": "en", "notifications": True}
@dataclass
class UserProfile:
user_id: int
username: str
settings: dict = field(default_factory=create_default_settings)
user_profile1 = UserProfile(user_id=201, username="charlie")
user_profile2 = UserProfile(user_id=202, username="david")
# Modify settings for user1 without affecting user2
user_profile1.settings["theme"] = "dark"
print(f"Charlie's settings: {user_profile1.settings}")
print(f"David's settings: {user_profile2.settings}")
μ΄κ²μ ν©ν 리 ν¨μκ° λ³΄λ€ λ³΅μ‘ν κΈ°λ³Έ μ΄κΈ°ν λ‘μ§μ μΊ‘μνν μ μλ λ°©λ²μ 보μ¬μ€λλ€. μ΄λ κΈ°λ³Έ μ€μ μ μ‘°μ νκ±°λ λμ μΌλ‘ κ²°μ ν μ μλλ‘ νμ¬ κ΅μ ν (i18n) λ° νμ§ν (l10n)μ λ§€μ° μ€μν©λλ€.
λ°μ΄ν° ꡬ쑰 νμ₯μ μν μμ νμ©
μμμ κ°μ²΄ μ§ν₯ νλ‘κ·Έλλ°μ μ΄μμΌλ‘, κΈ°μ‘΄ ν΄λμ€μμ μμ±κ³Ό λμμ μμνλ μ ν΄λμ€λ₯Ό λ§λ€ μ μμ΅λλ€. λ°μ΄ν° ν΄λμ€μ 컨ν μ€νΈμμ μμμ μ¬μ©νλ©΄ λ°μ΄ν° ꡬ쑰μ κ³μΈ΅ ꡬ쑰λ₯Ό ꡬμΆνμ¬ μ½λ μ¬μ¬μ©μ μ΄μ§νκ³ λ³΄λ€ μΌλ°μ μΈ λ°μ΄ν° λͺ¨λΈμ νΉμ λ²μ μ μ μν μ μμ΅λλ€.
λ°μ΄ν° ν΄λμ€ μμ μλ λ°©μ
λ°μ΄ν° ν΄λμ€κ° λ€λ₯Έ ν΄λμ€ (μΌλ° ν΄λμ€ λλ λ€λ₯Έ λ°μ΄ν° ν΄λμ€μΌ μ μμ)μμ μμλλ©΄ νλλ₯Ό μλμΌλ‘ μμν©λλ€. μμ±λ __init__
λ©μλμμ νλμ μμκ° μ€μν©λλ€. μμ ν΄λμ€μ νλκ° λ¨Όμ μ€κ³ κ·Έ λ€μ νμ ν΄λμ€μ νλκ° μ΅λλ€. μ΄ λμμ μΌλ°μ μΌλ‘ μΌκ΄λ μ΄κΈ°ν μμλ₯Ό μ μ§νλ λ° λ°λμ§ν©λλ€.
μμ : κΈ°λ³Έ μμ
κΈ°λ³Έ `Resource` λ°μ΄ν° ν΄λμ€λ‘ μμν λ€μ νΉμ λ²μ μ λ§λ€μ΄ λ³΄κ² μ΅λλ€.
from dataclasses import dataclass
@dataclass
class Resource:
resource_id: str
name: str
owner: str
@dataclass
class Server(Resource):
ip_address: str
os_type: str
@dataclass
class Database(Resource):
db_type: str
version: str
# Usage
server1 = Server(resource_id="srv-001", name="webserver-prod", owner="ops_team", ip_address="192.168.1.10", os_type="Linux")
db1 = Database(resource_id="db-005", name="customer_db", owner="db_admins", db_type="PostgreSQL", version="14.2")
print(server1)
# Output: Server(resource_id='srv-001', name='webserver-prod', owner='ops_team', ip_address='192.168.1.10', os_type='Linux')
print(db1)
# Output: Database(resource_id='db-005', name='customer_db', owner='db_admins', db_type='PostgreSQL', version='14.2')
μ¬κΈ°μ Server
λ° Database
λ μ체 νΉμ νλμ ν¨κ» Resource
κΈ°λ³Έ ν΄λμ€μμ resource_id
, name
λ° owner
νλλ₯Ό μλμΌλ‘ κ°μ΅λλ€.
νλ λ° μ΄κΈ°ν μμ
μμ±λ __init__
λ©μλλ νλκ° μ μλ μμλλ‘ μΈμλ₯Ό νμ©νμ¬ μμ 체μΈμ λ°λΌ μ΄λν©λλ€.
# The __init__ signature for Server would conceptually be:
# def __init__(self, resource_id: str, name: str, owner: str, ip_address: str, os_type: str): ...
# Initialization order matters:
# This would fail because Server expects parent fields first
# invalid_server = Server(ip_address="10.0.0.5", resource_id="srv-002", name="appserver", owner="devs", os_type="Windows")
@dataclass(eq=False)
λ° μμ
κΈ°λ³Έμ μΌλ‘ λ°μ΄ν° ν΄λμ€λ λΉκ΅λ₯Ό μν΄ __eq__
λ©μλλ₯Ό μμ±ν©λλ€. μμ ν΄λμ€μ eq=False
μ΄λ©΄ ν΄λΉ μμλ λλ±μ± λ©μλλ₯Ό μμ±νμ§ μμ΅λλ€. μμλ νλλ₯Ό ν¬ν¨ν λͺ¨λ νλλ₯Ό κΈ°λ°μΌλ‘ λλ±μ±μ μνλ©΄ eq=True
(κΈ°λ³Έκ°)λ₯Ό νμΈνκ±°λ νμν κ²½μ° μμ ν΄λμ€μμ λͺ
μμ μΌλ‘ μ€μ νμμμ€.
μμ λ° κΈ°λ³Έκ°
μμμ μμ ν΄λμ€μ μ μλ κΈ°λ³Έκ° λ° κΈ°λ³Έ ν©ν 리μ μλ²½νκ² μλν©λλ€.
from dataclasses import dataclass, field
from datetime import datetime
@dataclass
class Auditable:
created_at: datetime = field(default_factory=datetime.now)
created_by: str = "system"
@dataclass
class User(Auditable):
user_id: int
username: str
is_admin: bool = False
# Usage
user1 = User(user_id=301, username="eve")
# We can override defaults
user2 = User(user_id=302, username="frank", created_by="admin_user_1", is_admin=True)
print(user1)
# Output: User(user_id=301, username='eve', is_admin=False, created_at=datetime.datetime(2023, 10, 27, 10, 0, 0, ...), created_by='system')
print(user2)
# Output: User(user_id=302, username='frank', is_admin=True, created_at=datetime.datetime(2023, 10, 27, 10, 0, 1, ...), created_by='admin_user_1')
μ΄ μμμ User
λ Auditable
μμ created_at
λ° created_by
νλλ₯Ό μμν©λλ€. created_at
μ κΈ°λ³Έ ν©ν 리λ₯Ό μ¬μ©νμ¬ κ° μΈμ€ν΄μ€μ λν μ νμμ€ν¬νλ₯Ό 보μ₯νκ³ created_by
λ μ¬μ μν μ μλ κ°λ¨ν κΈ°λ³Έκ°μ κ°μ΅λλ€.
frozen=True
κ³ λ € μ¬ν
μμ λ°μ΄ν° ν΄λμ€κ° frozen=True
λ‘ μ μλ κ²½μ° μμνλ λͺ¨λ νμ λ°μ΄ν° ν΄λμ€λ κ³ μ λμ΄ μΈμ€ν΄μ€ν νμλ ν΄λΉ νλλ₯Ό μμ ν μ μμ΅λλ€. μ΄ λΆλ³μ±μ νΉν λμ μμ€ν
μμ λλ λ°μ΄ν°κ° μμ±λ νμλ λ³κ²½λμ§ μμμΌ νλ κ²½μ° λ°μ΄ν° 무결μ±μ μ μ©ν μ μμ΅λλ€.
μμμ μ¬μ©νλ κ²½μ°: νμ₯ λ° νΉμν
μμμ λ€μκ³Ό κ°μ κ²½μ°μ μ΄μμ μ λλ€.
- μ¬λ¬ κ°μ λ³΄λ€ κ΅¬μ²΄μ μΈ μ νμΌλ‘ νΉμννλ €λ μΌλ°μ μΈ λ°μ΄ν° κ΅¬μ‘°κ° μμ΅λλ€.
- κ΄λ ¨ λ°μ΄ν° μ νμμ κ³΅ν΅ νλ μ§ν©μ μ μ©νκ³ μΆμ΅λλ€.
- κ°λ κ³μΈ΅ ꡬ쑰 (μ: λ€μν μ νμ μλ¦Ό, λ€μν κ²°μ λ°©λ²)λ₯Ό λͺ¨λΈλ§νκ³ μμ΅λλ€.
ν©ν 리 ν¨μ vs. μμ: λΉκ΅ λΆμ
νλ ν©ν 리 ν¨μμ μμμ λͺ¨λ μ μ°νκ³ κ°λ ₯ν λ°μ΄ν° ν΄λμ€λ₯Ό λ§λλ κ°λ ₯ν λꡬμ΄μ§λ§ μλ‘ λ€λ₯Έ μ£Όμ λͺ©μ μ μνν©λλ€. νΉμ λͺ¨λΈλ§ μꡬ μ¬νμ λ§λ μ¬λ°λ₯Έ μ κ·Ό λ°©μμ μ ννλ €λ©΄ κ·Έ μ°¨μ΄μ μ μ΄ν΄νλ κ²μ΄ μ€μν©λλ€.
λͺ©μ λ° λ²μ
- ν©ν 리 ν¨μ: μ£Όλ‘ νΉμ νλμ λν κΈ°λ³Έκ°μ΄ μ΄λ»κ² μμ±λλμ§μ κ΄μ¬μ κ°μ΅λλ€. λ³κ²½ κ°λ₯ν κΈ°λ³Έκ°μ΄ μ¬λ°λ₯΄κ² μ²λ¦¬λλλ‘ νμ¬ κ° μΈμ€ν΄μ€μ λν μ κ°μ μ 곡ν©λλ€. κ·Έ λ²μλ μΌλ°μ μΌλ‘ κ°λ³ νλλ‘ μ νλ©λλ€.
- μμ: μμ ν΄λμ€μ νλλ₯Ό μ¬μ¬μ©νμ¬ ν΄λμ€κ° μ΄λ€ νλλ₯Ό κ°λμ§μ κ΄μ¬μ κ°μ΅λλ€. κΈ°μ‘΄ λ°μ΄ν° ꡬ쑰λ₯Ό μλ‘κ³ κ΄λ ¨λ κ΅¬μ‘°λ‘ νμ₯νκ³ νΉμννλ κ²μ λλ€. κ·Έ λ²μλ ν΄λμ€ μμ€μ μμΌλ©° μ ν κ°μ κ΄κ³λ₯Ό μ μν©λλ€.
μ μ°μ± λ° μ μμ±
- ν©ν 리 ν¨μ: νλλ₯Ό μ΄κΈ°ννλ λ° λ°μ΄λ μ μ°μ±μ μ 곡ν©λλ€. κ°λ¨ν λ΄μ₯ ν¨μ, λλ€ λλ 볡μ‘ν ν¨μλ₯Ό μ¬μ©νμ¬ κΈ°λ³Έ λ‘μ§μ μ μν μ μμ΅λλ€. μ΄λ κΈ°λ³Έκ°μ΄ 컨ν μ€νΈ (μ: λ‘μΊ, μ¬μ©μ κΈ°λ³Έ μ€μ )μ λ°λΌ λ¬λΌμ§ μ μλ κ΅μ νμ νΉν μ μ©ν©λλ€. μλ₯Ό λ€μ΄, κΈλ‘λ² κ΅¬μ±μ νμΈνλ ν©ν 리λ₯Ό μ¬μ©νμ¬ κΈ°λ³Έ ν΅νλ₯Ό μ€μ ν μ μμ΅λλ€.
- μμ: ꡬ쑰μ μ μ°μ±μ μ 곡ν©λλ€. λ°μ΄ν° μ νμ λΆλ₯λ₯Ό ꡬμΆν μ μμ΅λλ€. κΈ°μ‘΄ λ°μ΄ν° ꡬ쑰μ λ³νμΈ μλ‘μ΄ μꡬ μ¬νμ΄ λνλλ©΄ μμμ ν΅ν΄ κ³΅ν΅ νλλ₯Ό 볡μ νμ§ μκ³ λ μ½κ² μΆκ°ν μ μμ΅λλ€. μλ₯Ό λ€μ΄, κΈλ‘λ² μ μ μκ±°λ νλ«νΌμλ κΈ°λ³Έ `Product` λ°μ΄ν° ν΄λμ€κ° μμ μ μμΌλ©°, μ΄λ₯Ό μμνμ¬ νΉμ νλκ° μλ `PhysicalProduct`, `DigitalProduct` λ° `ServiceProduct`λ₯Ό λ§λ€ μ μμ΅λλ€.
μ½λ μ¬μ¬μ©μ±
- ν©ν 리 ν¨μ: κΈ°λ³Έκ°μ λν μ΄κΈ°ν λ‘μ§μ μ¬μ¬μ©μ±μ μ΄μ§ν©λλ€. μ΄κΈ°ν λ‘μ§μ΄ μΌλ°μ μΈ κ²½μ° μ μ μλ ν©ν 리 ν¨μλ₯Ό μ¬λ¬ νλ λλ λ€λ₯Έ λ°μ΄ν° ν΄λμ€μμλ μ¬μ¬μ©ν μ μμ΅λλ€.
- μμ: κΈ°λ³Έ ν΄λμ€μμ κ³΅ν΅ νλ λ° λμμ μ μνμ¬ μ½λ μ¬μ¬μ©μ νμνλ©° νμ ν΄λμ€μμ μλμΌλ‘ μ¬μ©ν μ μμ΅λλ€. μ΄λ κ² νλ©΄ μ¬λ¬ ν΄λμ€μμ λμΌν νλ μ μλ₯Ό λ°λ³΅νμ§ μμλ λ©λλ€.
볡μ‘μ± λ° μ μ§ κ΄λ¦¬μ±
- ν©ν 리 ν¨μ: κ°μ κ³μΈ΅μ μΆκ°ν μ μμ΅λλ€. λ¬Έμ λ₯Ό ν΄κ²°νμ§λ§ λλ²κΉ μλ ν©ν 리 ν¨μλ₯Ό μΆμ νλ μμ μ΄ ν¬ν¨λ μ μμ΅λλ€. κ·Έλ¬λ λͺ ννκ³ μ΄λ¦μ΄ μ μ§μ λ ν©ν 리μ κ²½μ° μΌλ°μ μΌλ‘ κ΄λ¦¬ν μ μμ΅λλ€.
- μμ: μ£Όμν΄μ κ΄λ¦¬νμ§ μμΌλ©΄ 볡μ‘ν ν΄λμ€ κ³μΈ΅ κ΅¬μ‘°λ‘ μ΄μ΄μ§ μ μμ΅λλ€ (μ: κΉμ μμ 체μΈ). MRO (λ©μλ νμΈ μμ)λ₯Ό μ΄ν΄νλ κ²μ΄ μ€μν©λλ€. μ λΉν κ³μΈ΅ ꡬ쑰μ κ²½μ° μ μ§ κ΄λ¦¬ κ°λ₯μ±μ΄ λκ³ κ°λ μ±μ΄ μ’μ΅λλ€.
λ κ°μ§ μ κ·Ό λ°©μ κ²°ν©
κ°μ₯ μ€μν κ²μ μ΄λ¬ν κΈ°λ₯μ΄ μνΈ λ°°νμ μ΄μ§ μλ€λ κ²μ λλ€. ν¨κ» μ¬μ©ν΄μΌ νλ κ²½μ°κ° λ§μ΅λλ€. νμ λ°μ΄ν° ν΄λμ€λ μμ ν΄λμ€μμ νλλ₯Ό μμν μ μμΌλ©° μ체 νλ μ€ νλ λλ νΉμν κΈ°λ³Έκ°μ΄ νμν κ²½μ° μμ ν΄λμ€μμ μμλ νλμ λν΄ ν©ν 리 ν¨μλ₯Ό μ¬μ©ν μλ μμ΅λλ€.
μμ : κ²°ν©λ μ¬μ©λ²
κΈλ‘λ² μ ν리μΌμ΄μ μμ λ€μν μ νμ μλ¦Όμ κ΄λ¦¬νκΈ° μν μμ€ν μ κ³ λ €ν΄ λ³΄κ² μ΅λλ€.
from dataclasses import dataclass, field
from datetime import datetime
import uuid
@dataclass
class BaseNotification:
notification_id: str = field(default_factory=lambda: str(uuid.uuid4()))
recipient_id: str
sent_at: datetime = field(default_factory=datetime.now)
message: str
read: bool = False
@dataclass
class EmailNotification(BaseNotification):
subject: str
sender_email: str
# Override parent's message with a more specific default if subject exists
message: str = field(init=False, default="") # Will be populated in __post_init__ or by other means
def __post_init__(self):
if not self.message: # If message wasn't explicitly set
self.message = f"{self.subject} - [Sent from {self.sender_email}]"
@dataclass
class SMSNotification(BaseNotification):
phone_number: str
sms_provider: str = "Twilio"
# Usage
email_notif = EmailNotification(recipient_id="user@example.com", subject="Your Order Shipped", sender_email="noreply@company.com")
sms_notif = SMSNotification(recipient_id="user123", phone_number="+15551234", message="Your package is out for delivery.")
print(f"Email: {email_notif}")
# Output will show a generated notification_id and sent_at, plus the auto-generated message
print(f"SMS: {sms_notif}")
# Output will show a generated notification_id and sent_at, with explicit message and sms_provider
μ΄ μμμ:
BaseNotification
μnotification_id
λ°sent_at
μ ν©ν 리 ν¨μλ₯Ό μ¬μ©ν©λλ€.EmailNotification
μBaseNotification
μμ μμλ°κ³message
νλλ₯Ό μ¬μ μνμ¬ λ€λ₯Έ νλλ₯Ό κΈ°λ°μΌλ‘ ꡬμ±νκΈ° μν΄__post_init__
λ₯Ό μ¬μ©νμ¬ λ³΄λ€ λ³΅μ‘ν μ΄κΈ°ν νλ¦μ 보μ¬μ€λλ€.SMSNotification
μ μμλ°κ³sms_provider
μ λν μ νμ κΈ°λ³Έκ°μ ν¬ν¨νμ¬ μ체 νΉμ νλλ₯Ό μΆκ°ν©λλ€.
μ΄λ¬ν μ‘°ν©μ ν΅ν΄ λ€μν μλ¦Ό μ νκ³Ό κ΅μ μ μꡬ μ¬νμ μ μν μ μλ ꡬ쑰νλκ³ μ¬μ¬μ© κ°λ₯νλ©° μ μ°ν λ°μ΄ν° λͺ¨λΈμ μ¬μ©ν μ μμ΅λλ€.
κΈλ‘λ² κ³ λ € μ¬ν λ° λͺ¨λ² μ¬λ‘
κΈλ‘λ² μ ν리μΌμ΄μ μ μν λ°μ΄ν° λͺ¨λΈμ μ€κ³ν λλ λ€μ μ¬νμ κ³ λ €νμμμ€.
- κΈ°λ³Έκ°μ νμ§ν: ν©ν 리 ν¨μλ₯Ό μ¬μ©νμ¬ λ‘μΊ λλ μ§μμ κΈ°λ°μΌλ‘ κΈ°λ³Έκ°μ κ²°μ ν©λλ€. μλ₯Ό λ€μ΄ κΈ°λ³Έ λ μ§ νμ, ν΅ν κΈ°νΈ λλ μΈμ΄ μ€μ μ μ κ΅ν ν©ν 리μμ μ²λ¦¬ν μ μμ΅λλ€.
- μκ°λ: νμμ€ν¬ν (
datetime
)λ₯Ό μ¬μ©ν λλ νμ μκ°λλ₯Ό μΌλμ λμμμ€. UTCμ μ μ₯νκ³ νμλ₯Ό μν΄ λ³ννλ κ²μ΄ μΌλ°μ μ΄κ³ κ°λ ₯ν λ°©λ²μ λλ€. ν©ν 리 ν¨μλ μΌκ΄μ±μ 보μ₯νλ λ° λμμ΄ λ μ μμ΅λλ€. - λ¬Έμμ΄μ κ΅μ ν: μ§μ μ μΈ λ°μ΄ν° ν΄λμ€ κΈ°λ₯μ μλμ§λ§ λ¬Έμμ΄ νλκ° λ²μμ μν΄ μ²λ¦¬λλ λ°©μμ κ³ λ €νμμμ€. λ°μ΄ν° ν΄λμ€λ νμ§νλ λ¬Έμμ΄μ λν ν€ λλ μ°Έμ‘°λ₯Ό μ μ₯ν μ μμ΅λλ€.
- λ°μ΄ν° μ ν¨μ± κ²μ¬: νΉν μ¬λ¬ κ΅κ°μ κ·μ μ°μ
μμ μ€μν λ°μ΄ν°μ κ²½μ° μ ν¨μ± κ²μ¬ λ‘μ§ ν΅ν©μ κ³ λ €νμμμ€. μ΄λ
__post_init__
λ©μλ λ΄μμ λλ μΈλΆ μ ν¨μ± κ²μ¬ λΌμ΄λΈλ¬λ¦¬λ₯Ό ν΅ν΄ μνν μ μμ΅λλ€. - API μ§ν: μμμ API λ²μ λλ λ€μν μλΉμ€ μμ€ κ³μ½μ κ΄λ¦¬νλ λ° κ°λ ₯ν μ μμ΅λλ€. κΈ°λ³Έ API μλ΅ λ°μ΄ν° ν΄λμ€κ° μκ³ v1, v2 λ±μ λν νΉμ ν΄λμ€κ° μκ±°λ λ€λ₯Έ ν΄λΌμ΄μΈνΈ κ³μΈ΅μ λν νΉμ ν΄λμ€κ° μμ μ μμ΅λλ€.
- λͺ λͺ κ·μΉ: νΉν μμλ ν΄λμ€μμ νλμ λν μΌκ΄λ λͺ λͺ κ·μΉμ μ μ§νμ¬ κΈλ‘λ² νμ κ°λ μ±μ ν₯μμν΅λλ€.
κ²°λ‘
Pythonμ dataclasses
λ λ°μ΄ν°λ₯Ό μ²λ¦¬νλ νλμ μ΄κ³ ν¨μ¨μ μΈ λ°©λ²μ μ 곡ν©λλ€. κΈ°λ³Έ μ¬μ©λ²μ κ°λ¨νμ§λ§ νλ ν©ν 리 ν¨μ λ° μμκ³Ό κ°μ κ³ κΈ κΈ°λ₯μ λ§μ€ν°νλ©΄ μ κ΅νκ³ μ μ°νλ©° μ μ§ κ΄λ¦¬ κ°λ₯ν λ°μ΄ν° λͺ¨λΈμ ꡬμΆν μ μλ μ§μ ν μ μ¬λ ₯μ λ°νν μ μμ΅λλ€.
νλ ν©ν 리 ν¨μλ λ³κ²½ κ°λ₯ν κΈ°λ³Έ νλλ₯Ό μ¬λ°λ₯΄κ² μ΄κΈ°ννμ¬ μΈμ€ν΄μ€ κ°μ λ°μ΄ν° 무결μ±μ 보μ₯νλ λ° μ ν©ν μ루μ μ λλ€. κ°μ²΄ μμ±μ κ°λ ₯νκ² λ§λλ λ° νμμ μΈ κΈ°λ³Έκ° μμ±μ λν μΈλΆνλ μ μ΄λ₯Ό μ 곡ν©λλ€.
λ°λ©΄μ μμμ κ³μΈ΅μ λ°μ΄ν° ꡬ쑰λ₯Ό λ§λ€κ³ μ½λ μ¬μ¬μ©μ μ΄μ§νλ©° κΈ°μ‘΄ λ°μ΄ν° λͺ¨λΈμ νΉμ λ²μ μ μ μνλ λ° κΈ°λ³Έμ λλ€. λ€λ₯Έ λ°μ΄ν° μ ν κ°μ λͺ νν κ΄κ³λ₯Ό ꡬμΆν μ μμ΅λλ€.
ν©ν 리 ν¨μμ μμμ λͺ¨λ μ΄ν΄νκ³ μ λ΅μ μΌλ‘ μ μ©ν¨μΌλ‘μ¨ κ°λ°μλ κΉ¨λνκ³ ν¨μ¨μ μΌ λΏλ§ μλλΌ κΈλ‘λ² μννΈμ¨μ΄ κ°λ°μ 볡μ‘νκ³ μ§ννλ μꡬμ κ³ λλ‘ μ μν μ μλ λ°μ΄ν° λͺ¨λΈμ λ§λ€ μ μμ΅λλ€. μ΄λ¬ν κΈ°λ₯μ νμ©νμ¬ λ³΄λ€ κ°λ ₯νκ³ μ μ§ κ΄λ¦¬ κ°λ₯νλ©° νμ₯ κ°λ₯ν Python μ½λλ₯Ό μμ±νμμμ€.